#pragma once
#include "../common/message.hpp"
#include "rpc_router.hpp"
#include <set>
namespace myrpc
{
    namespace server
    {
        class ProviderManager
        {
        public:
            using ptr = std::shared_ptr<ProviderManager>;
            struct Provider
            {
                using ptr = std::shared_ptr<Provider>;
                std::mutex _mutex; // 添加服务的时候，针对methods数组
                BaseConnection::ptr conn;
                Address host;
                std::vector<std::string> methods;
                Provider(const BaseConnection::ptr &c, const Address &h)
                    : conn(c),
                      host(h)
                {
                }
                void appendMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    methods.push_back(method);
                }
            };
            // 当一个新的服务提供进行服务注册的时候调用
            void addProvider(const BaseConnection::ptr &c, const Address &h, const std::string &method)
            {
                Provider::ptr provider;
                // 查找连接所关联的服务提供者对象，找到则获取，找不到则创建，并建立关联
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _conns.find(c);
                    if (it == _conns.end())
                    {
                        provider = std::make_shared<Provider>(c, h);
                        _conns.insert(std::make_pair(c, provider));
                    }
                    else
                    {
                        provider = it->second;
                    }

                    // method的方法的提供主机要多出一个，_provider新增数据
                    auto &providers = _providers[method];
                    providers.insert(provider);
                }
                // 向服务对象中新增一个所能提供的服务名称
                provider->appendMethod(method);
                
            }
            // 当一个服务提供者断开连接，获取它的信息，用于进行服务下线通知
            Provider::ptr getProvider(const BaseConnection::ptr &c)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conns.find(c);
                if (it == _conns.end())
                {
                    return Provider::ptr();
                }
                return it->second;
            }
            // 当一个服务提供者断开连接时，删除它的关联信息
            void delProvider(const BaseConnection::ptr &c)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _conns.find(c);
                if (it == _conns.end())
                {
                    return;
                }
                auto &provider = it->second;
                for (auto &method : provider->methods)
                {
                    auto &providers = _providers[method];
                    providers.erase(provider);
                }
                _conns.erase(it);
            }
            std::vector<Address> methodHosts(const std::string &method)
            {
                auto it=_providers.find(method);
                if(it==_providers.end())
                {
                    return std::vector<Address>();
                }
                std::vector<Address> result;
                for(auto &provider:it->second)
                {
                    result.push_back(provider->host);
                }
                return result;
            }
        private:
            std::mutex _mutex;
            std::unordered_map<BaseConnection::ptr, Provider::ptr> _conns;
            std::unordered_map<std::string, std::set<Provider::ptr>> _providers;
        };
        class DiscovererManager
        {
        public:
            using ptr = std::shared_ptr<DiscovererManager>;

            struct Discoverer
            {
                using ptr = std::shared_ptr<Discoverer>;
                BaseConnection::ptr conn;
                std::mutex _mutex;
                std::vector<std::string> methods; // 发现过的服务名称
                Discoverer(const BaseConnection::ptr &c)
                    : conn(c)
                {
                }
                void appendMethod(const std::string &method)
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    methods.push_back(method);
                }
            };
            // 当每次客户端进行服务发现的时候新增发现者，新增服务名称
            Discoverer::ptr addDiscoverer(const BaseConnection::ptr &c,const std::string &method)
            {
                Discoverer::ptr discoverer;
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it=_conns.find(c);
                    if(it==_conns.end())
                    {
                        discoverer=std::make_shared<Discoverer>(c);
                        _conns.insert(std::make_pair(c,discoverer));
                    }
                    else{
                        discoverer=it->second;
                    }
                    auto &methods=_discoverers[method];
                    methods.insert(discoverer);
                }
                discoverer->appendMethod(method);
                return discoverer;
            }
            // 发现者客户端断开连接时，找到发现者信息，删除关联数据
            void delDiscoverer(const BaseConnection::ptr &c)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_conns.find(c);
                if(it==_conns.end())
                {
                    //没有找到连接对应的发现者信息，代表客户端不是一个服务发现者
                    return ;
                }
                auto discoverer=it->second;
                for(auto method:discoverer->methods)
                {
                    auto methods=_discoverers[method];
                    methods.erase(discoverer);
                }

                _conns.erase(it);
            }
            // 当有一个新的服务提供者上线，则进行上线通知
            void onlineNotify(const std::string &method,const Address &host)
            {
                notify(method,host,ServiceOptype::SERVICE_ONLINE);
            }
            // 当有一个新的服务提供者下线，则进行下线通知
            void offlineNotify(const std::string &method,const Address &host)
            {
                notify(method,host,ServiceOptype::SERVICE_OFFLINE);
            }
        private:
            void notify(const std::string &method,const Address &host,ServiceOptype optype)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it=_discoverers.find(method);
                if(it==_discoverers.end())
                {
                    return;
                }
                auto req_msg=myrpc::MessageFactory::create<ServiceRequest>();
                req_msg->setId(UUID::uuid());
                req_msg->setHost(host);
                req_msg->setMethod(method);
                req_msg->setMType(MType::REQ_SERVICE);
                req_msg->setServiceOptype(optype);
                for(auto &discoverer:it->second)
                {
                    discoverer->conn->send(req_msg);
                }
            }
        private:
            std::mutex _mutex;
            std::unordered_map<BaseConnection::ptr, Discoverer::ptr> _conns;
            std::unordered_map<std::string, std::set<Discoverer::ptr>> _discoverers; // 记录某个服务谁发现过
        };
        class PDManager
        {
        public:
            using ptr = std::shared_ptr<PDManager>;
            PDManager() 
                :_providers(std::make_shared<ProviderManager>()),
                _discoverers(std::make_shared<DiscovererManager>())
            {}
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                //服务操作请求：服务注册/服务发现
                ServiceOptype optype=msg->serviceOptype();
                if(optype==ServiceOptype::SERVICE_REGISTRY)
                {
                    //服务注册
                    //1.新增服务者  2.进行服务上线通知
                    ILOG("%s:%d 注册服务 %s", msg->host().first.c_str(), msg->host().second, msg->method().c_str());
                    _providers->addProvider(conn,msg->host(),msg->method());
                    _discoverers->onlineNotify(msg->method(),msg->host());
                    //响应
                    return registryResponse(conn,msg);
                }
                else if(optype==ServiceOptype::SERVICE_DISCOVER)
                {
                    //服务发现
                    //1.新增服务发现者
                    ILOG("客户端要进行 %s 服务发现！", msg->method().c_str());
                    _discoverers->addDiscoverer(conn,msg->method());
                    //响应
                    return discoveryResponse(conn,msg);
                }
                else{
                    ELOG("收到服务操作请求，但是操作类型错误！");
                    return errResponse(conn,msg);
                }
            }
            void onConnShutdown(const BaseConnection::ptr &conn)
            {
                auto provider=_providers->getProvider(conn);
                if(provider!=nullptr)
                {
                    ILOG("%s:%d 服务下线", provider->host.first.c_str(), provider->host.second);
                    for(auto &method:provider->methods)
                    {
                        _discoverers->offlineNotify(method,provider->host);
                    }
                    _providers->delProvider(conn);
                }
                _discoverers->delDiscoverer(conn);
            }
        private:
            void errResponse(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
            {
                auto msg_rsp=MessageFactory::create<ServiceResponse>();
                msg_rsp->setId(msg->rid());
                msg_rsp->setMType(MType::REQ_SERVICE);
                msg_rsp->setServiceOptype(ServiceOptype::SERVICE_UNKNOW);
                msg_rsp->setRcode(RCode::RCODE_INVALID_OPTYPE);

                conn->send(msg_rsp);                
            }
            void registryResponse(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
            {
                auto msg_rsp=MessageFactory::create<ServiceResponse>();
                msg_rsp->setId(msg->rid());
                msg_rsp->setMType(MType::RSP_SERVICE);
                msg_rsp->setServiceOptype(ServiceOptype::SERVICE_REGISTRY);
                msg_rsp->setRcode(RCode::RCODE_OK);

                conn->send(msg_rsp);
            }
            void discoveryResponse(const BaseConnection::ptr &conn,const ServiceRequest::ptr &msg)
            {
                auto msg_rsp=MessageFactory::create<ServiceResponse>();
                msg_rsp->setId(msg->rid());
                msg_rsp->setMType(MType::RSP_SERVICE);
                msg_rsp->setServiceOptype(ServiceOptype::SERVICE_DISCOVER);
                auto hosts=_providers->methodHosts(msg->method());
                if(hosts.empty())
                {
                    msg_rsp->setRcode(RCode::RCODE_NOT_FOUND_SERVICE);
                    conn->send(msg_rsp);
                    return;
                }
                msg_rsp->setRcode(RCode::RCODE_OK);
                msg_rsp->setHost(hosts);
                msg_rsp->setMethod(msg->method());
                conn->send(msg_rsp);
            }
        private:
            ProviderManager::ptr _providers;
            DiscovererManager::ptr _discoverers;
        };
    }
}